import os
import json
from PySide6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QPushButton, QFileDialog,
    QTextEdit, QLabel, QLineEdit, QHBoxLayout, QGroupBox,
    QProgressBar, QMessageBox, QComboBox, QListWidget, QDialog,
    QDialogButtonBox, QFormLayout, QListWidgetItem, QSplitter,
    QTabWidget, QCheckBox
)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QFont, QAction

class PresetDialog(QDialog):
    def __init__(self, presets, parent=None):
        super().__init__(parent)
        self.presets = presets
        self.setWindowTitle("管理预设")
        self.resize(500, 400)
        
        layout = QVBoxLayout()
        
        # 预设列表
        layout.addWidget(QLabel("预设列表:"))
        self.preset_list = QListWidget()
        self.update_preset_list()
        layout.addWidget(self.preset_list)
        
        # 按钮
        button_layout = QHBoxLayout()
        self.add_btn = QPushButton("添加预设")
        self.edit_btn = QPushButton("编辑预设")
        self.delete_btn = QPushButton("删除预设")
        self.add_btn.clicked.connect(self.add_preset)
        self.edit_btn.clicked.connect(self.edit_preset)
        self.delete_btn.clicked.connect(self.delete_preset)
        
        button_layout.addWidget(self.add_btn)
        button_layout.addWidget(self.edit_btn)
        button_layout.addWidget(self.delete_btn)
        layout.addLayout(button_layout)
        
        # 对话框按钮
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)
        
        self.setLayout(layout)
    
    def update_preset_list(self):
        self.preset_list.clear()
        for name in self.presets.keys():
            self.preset_list.addItem(name)
    
    def add_preset(self):
        dialog = PresetEditDialog(self.presets, self)
        if dialog.exec() == QDialog.Accepted:
            name, extensions = dialog.get_preset_data()
            if name and name not in self.presets:
                self.presets[name] = extensions
                self.update_preset_list()
    
    def edit_preset(self):
        current_item = self.preset_list.currentItem()
        if not current_item:
            QMessageBox.warning(self, "警告", "请选择一个预设进行编辑")
            return
        
        preset_name = current_item.text()
        dialog = PresetEditDialog(self.presets, self, preset_name)
        if dialog.exec() == QDialog.Accepted:
            new_name, extensions = dialog.get_preset_data()
            if new_name != preset_name and new_name in self.presets:
                QMessageBox.warning(self, "警告", "预设名称已存在")
                return
            
            # 删除旧的，添加新的
            if new_name != preset_name:
                del self.presets[preset_name]
            self.presets[new_name] = extensions
            self.update_preset_list()
    
    def delete_preset(self):
        current_item = self.preset_list.currentItem()
        if not current_item:
            QMessageBox.warning(self, "警告", "请选择要删除的预设")
            return
        
        preset_name = current_item.text()
        reply = QMessageBox.question(
            self, "确认删除", 
            f"确定要删除预设 '{preset_name}' 吗？",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            del self.presets[preset_name]
            self.update_preset_list()

class PresetEditDialog(QDialog):
    def __init__(self, presets, parent=None, preset_name=None):
        super().__init__(parent)
        self.presets = presets
        self.preset_name = preset_name
        
        self.setWindowTitle("编辑预设" if preset_name else "添加预设")
        self.resize(400, 300)
        
        layout = QFormLayout()
        
        # 预设名称
        self.name_input = QLineEdit()
        if preset_name:
            self.name_input.setText(preset_name)
        layout.addRow("预设名称:", self.name_input)
        
        # 常见扩展名选择
        layout.addRow(QLabel("常用文件类型:"))
        
        # 预定义的常见文件类型
        self.common_extensions = {
            "Python编译文件": [".pyc", ".pyo", "__pycache__"],
            "Python包文件": [".egg", ".whl", "dist/", "build/"],
            "C++编译文件": [".obj", ".o", ".exe", ".dll", ".lib", ".so", ".a"],
            "C++构建文件": ["CMakeFiles/", "cmake_install.cmake", "CMakeCache.txt", "Makefile"],
            "Java编译文件": [".class", ".jar"],
            "日志文件": [".log", ".tmp", ".temp"],
            "IDE文件": [".vscode/", ".idea/", "*.swp", "*.swo"],
            "版本控制": [".git/", ".svn/"],
            "系统文件": [".DS_Store", "Thumbs.db"]
        }
        
        for category, exts in self.common_extensions.items():
            checkbox = QCheckBox(category)
            checkbox.extensions = exts
            checkbox.toggled.connect(self.on_category_toggled)
            layout.addRow(checkbox)
        
        # 自定义扩展名
        layout.addRow(QLabel("自定义文件类型（每行一个）:"))
        self.custom_input = QTextEdit()
        self.custom_input.setMaximumHeight(100)
        if preset_name and preset_name in self.presets:
            # 显示已有的自定义设置
            existing_exts = self.presets[preset_name]
            custom_text = "\n".join([ext for ext in existing_exts if not any(ext in common for common in self.common_extensions.values())])
            self.custom_input.setPlainText(custom_text)
        layout.addRow(self.custom_input)
        
        # 按钮
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addRow(self.button_box)
        
        self.setLayout(layout)
    
    def on_category_toggled(self, checked):
        # 这里可以实时更新预览，但为了简单起见，我们只在确定时处理
        pass
    
    def get_preset_data(self):
        name = self.name_input.text().strip()
        
        # 收集所有选中的扩展名
        extensions = []
        
        # 从复选框收集
        for checkbox in self.findChildren(QCheckBox):
            if checkbox.isChecked():
                extensions.extend(checkbox.extensions)
        
        # 从自定义输入收集
        custom_text = self.custom_input.toPlainText().strip()
        if custom_text:
            custom_exts = [line.strip() for line in custom_text.split('\n') if line.strip()]
            extensions.extend(custom_exts)
        
        return name, extensions

class ScanThread(QThread):
    """扫描线程，避免界面卡顿"""
    progress_signal = Signal(int)
    text_signal = Signal(str)
    finished_signal = Signal(str)
    
    def __init__(self, root_dir, ignore_extensions):
        super().__init__()
        self.root_dir = root_dir
        self.ignore_extensions = ignore_extensions
        
    def run(self):
        result_text = []
        total_files = 0
        processed_files = 0
        
        # 先统计文件总数
        for dirpath, dirnames, filenames in os.walk(self.root_dir):
            for filename in filenames:
                if not self.should_ignore(filename, dirpath):
                    total_files += 1
        
        for dirpath, dirnames, filenames in os.walk(self.root_dir):
            # 在遍历过程中也要检查目录是否需要忽略
            dirnames[:] = [d for d in dirnames if not self.should_ignore(d, dirpath, is_dir=True)]
            filenames.sort()
            
            for filename in filenames:
                if self.should_ignore(filename, dirpath):
                    continue

                file_path = os.path.join(dirpath, filename)
                relative_path = os.path.relpath(file_path, self.root_dir)
                
                result_text.append(f"=====FILE_START: {relative_path}=====")
                try:
                    with open(file_path, "r", encoding="utf-8") as f:
                        result_text.append(f.read())
                except Exception as e:
                    result_text.append(f"// 无法读取文件: {e}")
                result_text.append(f"=====FILE_END=====\n")
                
                processed_files += 1
                progress = int((processed_files / total_files) * 100) if total_files > 0 else 0
                self.progress_signal.emit(progress)
        
        final_text = "\n".join(result_text)
        self.finished_signal.emit(final_text)
    
    def should_ignore(self, name, dirpath, is_dir=False):
        """检查文件或目录是否应该被忽略"""
        if is_dir:
            # 检查完整路径是否匹配任何忽略模式
            full_path = os.path.join(dirpath, name)
            relative_path = os.path.relpath(full_path, self.root_dir) + '/'
            for pattern in self.ignore_extensions:
                if pattern.endswith('/') and relative_path.startswith(pattern.rstrip('/') + '/'):
                    return True
                elif pattern == name + '/':
                    return True
            return False
        else:
            # 文件检查
            for pattern in self.ignore_extensions:
                if pattern.startswith('*'):
                    # 通配符模式
                    if name.endswith(pattern[1:]) or name == pattern[1:]:
                        return True
                elif pattern.endswith('/'):
                    # 目录模式，文件不应该匹配目录模式
                    continue
                elif name.endswith(pattern) or name == pattern:
                    return True
            return False

class FileTool(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("文件扫描与恢复工具 - Professional")
        self.resize(1000, 700)
        
        # 预设管理
        self.presets = self.load_presets()
        
        # 默认屏蔽类型
        self.ignore_extensions = [".pyc", "__pycache__", ".pyo"]
        self.scan_thread = None

        self.setup_ui()
        self.setup_styles()

    def setup_ui(self):
        main_layout = QVBoxLayout()
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)

        # 标题
        title_label = QLabel("文件扫描与恢复工具")
        title_label.setAlignment(Qt.AlignCenter)
        title_font = QFont()
        title_font.setPointSize(16)
        title_font.setBold(True)
        title_label.setFont(title_font)
        title_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
        main_layout.addWidget(title_label)

        # 使用选项卡组织功能
        tab_widget = QTabWidget()
        
        # 扫描选项卡
        scan_tab = QWidget()
        self.setup_scan_tab(scan_tab)
        tab_widget.addTab(scan_tab, "文件扫描")
        
        # 恢复选项卡
        restore_tab = QWidget()
        self.setup_restore_tab(restore_tab)
        tab_widget.addTab(restore_tab, "文件恢复")
        
        main_layout.addWidget(tab_widget)
        
        # 状态栏
        self.status_label = QLabel("就绪")
        self.status_label.setStyleSheet("color: #666; font-size: 9pt; padding: 5px;")
        main_layout.addWidget(self.status_label)

        self.setLayout(main_layout)

    def setup_scan_tab(self, tab):
        layout = QVBoxLayout()
        
        # 预设设置分组
        preset_group = QGroupBox("预设设置")
        preset_layout = QVBoxLayout()

        # 预设选择行
        preset_select_layout = QHBoxLayout()
        preset_select_layout.addWidget(QLabel("项目预设:"))
        
        self.preset_combo = QComboBox()
        self.preset_combo.addItems(self.presets.keys())
        self.preset_combo.currentTextChanged.connect(self.on_preset_changed)
        preset_select_layout.addWidget(self.preset_combo)
        
        self.manage_preset_btn = QPushButton("管理预设")
        self.manage_preset_btn.clicked.connect(self.manage_presets)
        preset_select_layout.addWidget(self.manage_preset_btn)
        
        preset_layout.addLayout(preset_select_layout)

        # 屏蔽类型显示和编辑
        preset_layout.addWidget(QLabel("当前屏蔽的文件类型:"))
        self.ignore_input = QTextEdit()
        self.ignore_input.setMaximumHeight(80)
        self.ignore_input.setPlaceholderText("每行输入一个要屏蔽的文件类型或模式...")
        preset_layout.addWidget(self.ignore_input)

        preset_group.setLayout(preset_layout)
        layout.addWidget(preset_group)

        # 扫描设置分组
        scan_group = QGroupBox("扫描设置")
        scan_layout = QVBoxLayout()

        # 扫描目录选择
        scan_dir_layout = QHBoxLayout()
        scan_dir_layout.addWidget(QLabel("扫描目录:"))
        self.scan_dir_input = QLineEdit()
        self.scan_dir_input.setPlaceholderText("请选择要扫描的目录...")
        scan_dir_layout.addWidget(self.scan_dir_input)
        self.scan_btn = QPushButton("浏览")
        self.scan_btn.clicked.connect(self.choose_scan_dir)
        scan_dir_layout.addWidget(self.scan_btn)
        scan_layout.addLayout(scan_dir_layout)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        scan_layout.addWidget(self.progress_bar)

        # 扫描按钮
        self.scan_generate_btn = QPushButton("开始扫描并生成文本")
        self.scan_generate_btn.clicked.connect(self.scan_and_generate)
        self.scan_generate_btn.setStyleSheet("background-color: #2196F3;")
        scan_layout.addWidget(self.scan_generate_btn)

        scan_group.setLayout(scan_layout)
        layout.addWidget(scan_group)

        # 文本显示区域
        text_group = QGroupBox("文件内容预览")
        text_layout = QVBoxLayout()
        self.scan_text = QTextEdit()
        self.scan_text.setPlaceholderText("扫描结果将显示在这里...")
        text_layout.addWidget(self.scan_text)
        text_group.setLayout(text_layout)
        layout.addWidget(text_group)

        tab.setLayout(layout)

    def setup_restore_tab(self, tab):
        layout = QVBoxLayout()
        
        restore_group = QGroupBox("文件恢复")
        restore_layout = QVBoxLayout()

        # 文本文件选择
        restore_file_layout = QHBoxLayout()
        restore_file_layout.addWidget(QLabel("文本文件:"))
        self.restore_file_input = QLineEdit()
        self.restore_file_input.setPlaceholderText("请选择包含文件数据的文本文件...")
        restore_file_layout.addWidget(self.restore_file_input)
        self.restore_btn = QPushButton("浏览")
        self.restore_btn.clicked.connect(self.choose_restore_file)
        restore_file_layout.addWidget(self.restore_btn)
        restore_layout.addLayout(restore_file_layout)

        # 输出目录
        output_layout = QHBoxLayout()
        output_layout.addWidget(QLabel("输出目录:"))
        self.output_dir_input = QLineEdit("recovered_files")
        output_layout.addWidget(self.output_dir_input)
        restore_layout.addLayout(output_layout)

        # 恢复按钮
        self.restore_generate_btn = QPushButton("从文本生成文件")
        self.restore_generate_btn.clicked.connect(self.restore_files)
        self.restore_generate_btn.setStyleSheet("background-color: #FF9800;")
        restore_layout.addWidget(self.restore_generate_btn)

        restore_group.setLayout(restore_layout)
        layout.addWidget(restore_group)
        
        tab.setLayout(layout)

    def setup_styles(self):
        self.setStyleSheet("""
            QWidget {
                font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
                font-size: 10pt;
            }
            QGroupBox {
                font-weight: bold;
                border: 2px solid #cccccc;
                border-radius: 8px;
                margin-top: 1ex;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px 0 5px;
            }
            QPushButton {
                background-color: #4CAF50;
                border: none;
                color: white;
                padding: 8px 16px;
                border-radius: 4px;
                font-weight: bold;
                min-width: 80px;
            }
            QPushButton:hover {
                background-color: #45a049;
            }
            QPushButton:pressed {
                background-color: #3d8b40;
            }
            QPushButton:disabled {
                background-color: #cccccc;
                color: #666666;
            }
            QTextEdit, QLineEdit, QComboBox {
                border: 1px solid #cccccc;
                border-radius: 4px;
                padding: 6px;
            }
            QLabel {
                color: #333333;
            }
            QProgressBar {
                border: 1px solid #cccccc;
                border-radius: 4px;
                text-align: center;
            }
            QProgressBar::chunk {
                background-color: #4CAF50;
                border-radius: 3px;
            }
            QTabWidget::pane {
                border: 1px solid #cccccc;
                border-radius: 4px;
            }
            QTabBar::tab {
                background-color: #f0f0f0;
                padding: 8px 16px;
                margin-right: 2px;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
            }
            QTabBar::tab:selected {
                background-color: #4CAF50;
                color: white;
            }
        """)

    def load_presets(self):
        """加载预设配置"""
        default_presets = {
            "Python项目": [".pyc", ".pyo", "__pycache__", "*.pyc", "dist/", "build/", ".egg-info/"],
            "C++项目": [".obj", ".o", ".exe", ".dll", ".lib", ".so", ".a", "CMakeFiles/", "cmake_install.cmake", "CMakeCache.txt", "Makefile"],
            "Java项目": [".class", ".jar", ".war", ".ear", "target/"],
            "Web项目": [".min.js", ".min.css", ".map", ".log", "node_modules/", ".npm"],
            "通用项目": [".log", ".tmp", ".temp", ".DS_Store", "Thumbs.db", ".git/", ".svn/"]
        }
        
        # 这里可以添加从文件加载预设的逻辑
        # 暂时返回默认预设
        return default_presets

    def save_presets(self):
        """保存预设到文件"""
        # 这里可以添加保存预设到文件的逻辑
        pass

    def on_preset_changed(self, preset_name):
        """当预设改变时更新屏蔽类型"""
        if preset_name in self.presets:
            self.ignore_extensions = self.presets[preset_name]
            # 更新显示
            self.ignore_input.setPlainText("\n".join(self.ignore_extensions))

    def manage_presets(self):
        """打开预设管理对话框"""
        dialog = PresetDialog(self.presets, self)
        if dialog.exec() == QDialog.Accepted:
            # 更新预设下拉框
            self.preset_combo.clear()
            self.preset_combo.addItems(self.presets.keys())
            # 保存预设
            self.save_presets()

    def choose_scan_dir(self):
        folder = QFileDialog.getExistingDirectory(self, "选择扫描目录")
        if folder:
            self.scan_dir_input.setText(folder)
            self.status_label.setText(f"已选择目录: {folder}")

    def scan_and_generate(self):
        root_dir = self.scan_dir_input.text()
        if not root_dir or not os.path.exists(root_dir):
            QMessageBox.warning(self, "警告", "请选择有效的扫描目录！")
            return

        # 从文本框获取屏蔽类型（每行一个）
        ignore_text = self.ignore_input.toPlainText().strip()
        if ignore_text:
            self.ignore_extensions = [line.strip() for line in ignore_text.split('\n') if line.strip()]

        # 禁用按钮，显示进度条
        self.scan_generate_btn.setEnabled(False)
        self.progress_bar.setVisible(True)
        self.status_label.setText("正在扫描文件，请稍候...")

        # 创建并启动扫描线程
        self.scan_thread = ScanThread(root_dir, self.ignore_extensions)
        self.scan_thread.progress_signal.connect(self.update_progress)
        self.scan_thread.finished_signal.connect(self.scan_finished)
        self.scan_thread.start()

    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def scan_finished(self, final_text):
        self.scan_text.setPlainText(final_text)
        
        # 保存文件
        output_file = "all_files_combined.txt"
        try:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write(final_text)
            self.status_label.setText(f"扫描完成！结果已保存到: {output_file}")
        except Exception as e:
            self.status_label.setText(f"保存文件时出错: {str(e)}")
        
        # 恢复UI状态
        self.scan_generate_btn.setEnabled(True)
        self.progress_bar.setVisible(False)
        QMessageBox.information(self, "完成", f"文件扫描完成！\n共处理了 {final_text.count('=====FILE_START:')} 个文件。")

    def choose_restore_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择文本文件", "", 
            "Text Files (*.txt);;All Files (*)"
        )
        if file_path:
            self.restore_file_input.setText(file_path)
            self.status_label.setText(f"已选择文件: {os.path.basename(file_path)}")

    def restore_files(self):
        input_file = self.restore_file_input.text()
        output_root = self.output_dir_input.text() or "recovered_files"
        
        if not input_file or not os.path.exists(input_file):
            QMessageBox.warning(self, "警告", "请选择有效的文本文件！")
            return

        try:
            # 显示进度
            self.restore_generate_btn.setEnabled(False)
            self.status_label.setText("正在恢复文件...")

            files_restored = self.restore_files_from_text(input_file, output_root)
            
            self.status_label.setText(f"恢复完成！共恢复了 {files_restored} 个文件到: {output_root}")
            QMessageBox.information(self, "完成", 
                                  f"文件恢复完成！\n共恢复了 {files_restored} 个文件到目录:\n{output_root}")
            
        except Exception as e:
            self.status_label.setText(f"恢复文件时出错: {str(e)}")
            QMessageBox.critical(self, "错误", f"恢复文件时出现错误:\n{str(e)}")
        finally:
            self.restore_generate_btn.setEnabled(True)

    def restore_files_from_text(self, input_file, output_root):
        if not os.path.exists(output_root):
            os.makedirs(output_root)

        current_file = None
        content_lines = []
        files_restored = 0

        with open(input_file, "r", encoding="utf-8") as f:
            for line in f:
                line_strip = line.strip()
                if line_strip.startswith("=====FILE_START:"):
                    if current_file:
                        self.write_file(current_file, content_lines)
                        files_restored += 1
                        content_lines = []
                    relative_path = line_strip[len("=====FILE_START:"):].replace("=====", "").strip()
                    current_file = os.path.join(output_root, relative_path)
                elif line_strip == "=====FILE_END=====":
                    if current_file:
                        self.write_file(current_file, content_lines)
                        files_restored += 1
                        current_file = None
                        content_lines = []
                else:
                    if current_file is not None:
                        content_lines.append(line)

        # 处理最后一个文件
        if current_file and content_lines:
            self.write_file(current_file, content_lines)
            files_restored += 1

        return files_restored

    def write_file(self, file_path, lines):
        os.makedirs(os.path.dirname(file_path), exist_ok=True)
        with open(file_path, "w", encoding="utf-8") as f:
            f.writelines(lines)


if __name__ == "__main__":
    app = QApplication([])
    
    # 设置应用程序样式
    app.setStyle('Fusion')
    
    window = FileTool()
    window.show()
    app.exec()